package gameplay;

import java.util.Random;

//import generator.Board;
//import generator.Generator;

import com.example.sudokusolver.R;

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.Toast;

public class Game extends Activity
{
	public static final String KEY_DIFFICULTY = "org.example.sudoku.difficulty";
	
	private static final String PREF_PUZZLE = "puzzle" ;
	
	public static final int DIFFICULTY_EASY = 0;
	public static final int DIFFICULTY_MEDIUM = 1;
	public static final int DIFFICULTY_HARD = 2;
	
	private int puzzle[] = new int[9 * 9];
	
	private String[] easyPuzzle;
	private String[] mediumPuzzle;
	private String[] hardPuzzle;
	
	private PuzzleView puzzleView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		
		initPuzzles();
		
		int diff = getIntent().getIntExtra(KEY_DIFFICULTY, DIFFICULTY_EASY);
		puzzle = getPuzzle(diff);
		calculateUsedTiles();
		
		puzzleView = new PuzzleView(this);
		setContentView(puzzleView);
		puzzleView.requestFocus();
	}
	
	@Override
	protected void onResume()
	{
		super.onResume();
		
	}
	
	@Override
	protected void onPause()
	{
		super.onPause();
		
		// Save the current puzzle
		getPreferences(MODE_PRIVATE).edit().putString(PREF_PUZZLE, toPuzzleString(puzzle)).commit();
	}
	
	/**
	 * Given a difficulty level, come up with a new puzzle
	 * @param diff
	 * @return int[]
	 */
	private int[] getPuzzle(int diff)
	{
		String puz;
		Random r = new Random();
		switch (diff)
		{
			case DIFFICULTY_HARD:
//				Generator generator = new Generator();
//				generator.generateBoard(2);
//				Board board = generator.getBoard();
//				board.printBoard();
//				hardPuzzle = board.getPuzzle();
				puz = hardPuzzle[r.nextInt(5)];
				break;
			case DIFFICULTY_MEDIUM:
//				Generator generator = new Generator();
//				generator.generateBoard(2);
//				Board board = generator.getBoard();
//				board.printBoard();
//				mediumPuzzle = board.getPuzzle();
				puz = mediumPuzzle[r.nextInt(5)];
				break;
			case DIFFICULTY_EASY:
//				Generator generator = new Generator();
//				generator.generateBoard(1);
//				Board board = generator.getBoard();
//				board.printBoard();
//				easyPuzzle = board.getPuzzle();
				puz = easyPuzzle[r.nextInt(5)];
				break;
			default:
				puz = easyPuzzle[r.nextInt(5)];
				break;
		}
		return fromPuzzleString(puz);
	}
	
	/**
	 * Convert an array into a puzzle string
	 * @param puz
	 * @return String
	 */
	static private String toPuzzleString(int[] puz)
	{
		StringBuilder buf = new StringBuilder();
		for (int element : puz)
		{
			buf.append(element);
		}
		return buf.toString();
	}
	
	/**
	 * Convert a puzzle string into an array
	 * @param string
	 * @return int[]
	 */
	static protected int[] fromPuzzleString(String string)
	{
		int[] puz = new int[string.length()];
		for (int i = 0; i < puz.length; i++)
		{
			puz[i] = string.charAt(i) - '0';
		}
		return puz;
	}
	
	/**
	 * Return the tile at the given coordinates
	 * @param x
	 * @param y
	 * @return int
	 */
	private int getTile(int x, int y)
	{
		return puzzle[y * 9 + x];
	}
	
	/**
	 * Change the tile at the given coordinates
	 * @param x
	 * @param y
	 * @param value
	 */
	private void setTile(int x, int y, int value)
	{
		puzzle[y * 9 + x] = value;
	}
	
	/**
	 * Return a string for the tile at the given coordinates
	 * @param x
	 * @param y
	 * @return String
	 */
	protected String getTileString(int x, int y)
	{
		int v = getTile(x, y);
		if (v == 0)
		{
			return "";
		}
		else
		{
			return String.valueOf(v);
		}
	}
	
	/**
	 * Change the tile only if it's a valid move
	 * @param x
	 * @param y
	 * @param value
	 * @return boolean
	 */
	protected boolean setTileIfValid(int x, int y, int value)
	{
		int tiles[] = getUsedTiles(x, y);
		if (value != 0)
		{
			for (int tile : tiles)
			{
				if (tile == value)
					return false;
			}
		}
		setTile(x, y, value);
		calculateUsedTiles();
		return true;
	}
	
	/**
	 * Open the keypad if there are any valid moves
	 * @param x
	 * @param y
	 */
	protected void showKeypadOrError(int x, int y)
	{
		int tiles[] = getUsedTiles(x, y);
		if (tiles.length == 9)
		{
			Toast toast = Toast.makeText(this, R.string.no_moves_label, Toast.LENGTH_SHORT);
			toast.setGravity(Gravity.CENTER, 0, 0);
			toast.show();
		}
		else
		{
			Dialog v = new Keypad(this, tiles, puzzleView);
			v.show();
		}
	}
	
	/** Cache of used tiles */
	private final int used[][][] = new int[9][9][];
	
	/**
	 * Return cached used tiles visible from the given coords
	 * @param x
	 * @param y
	 * @return int[]
	 */
	protected int[] getUsedTiles(int x, int y)
	{
		return used[x][y];
	}
	
	/** Compute the two dimensional array of used tiles */
	private void calculateUsedTiles()
	{
		for (int x = 0; x < 9; x++)
		{
			for (int y = 0; y < 9; y++)
			{
				used[x][y] = calculateUsedTiles(x, y);
				// Log.d(TAG, "used[" + x + "][" + y + "] = "
				// + toPuzzleString(used[x][y]));
			}
		}
	}
	
	/**
	 * Compute the used tiles visible from this position
	 * @param x
	 * @param y
	 * @return int[]
	 */
	private int[] calculateUsedTiles(int x, int y)
	{
		int c[] = new int[9];
		// horizontal
		for (int i = 0; i < 9; i++)
		{ 
			if (i == y)
			{
				continue;
			}
			int t = getTile(x, i);
			if (t != 0)
			{
				c[t - 1] = t;
			}
		}
		// vertical
		for (int i = 0; i < 9; i++)
		{ 
			if (i == x)
			{
				continue;
			}
			int t = getTile(i, y);
			if (t != 0)
			{
				c[t - 1] = t;
			}
		}
		// same cell block
		int startx = (x / 3) * 3; 
		int starty = (y / 3) * 3;
		for (int i = startx; i < startx + 3; i++)
		{
			for (int j = starty; j < starty + 3; j++)
			{
				if (i == x && j == y)
				{
					continue;
				}
				int t = getTile(i, j);
				if (t != 0)
				{
					c[t - 1] = t;
				}
			}
		}
		// compress
		int nused = 0; 
		for (int t : c)
		{
			if (t != 0)
			{
				nused++;
			}
		}
		int c1[] = new int[nused];
		nused = 0;
		for (int t : c)
		{
			if (t != 0)
			{
				c1[nused++] = t;
			}
		}
		return c1;
	}
	
	public void initPuzzles()
	{
		easyPuzzle = new String[]
			{
				"360000000004230800000004200070460003820000014500013020001900000007048300000000045",
				"459701063030009085806203091100508934090000570500307608005032046010800000923160857",
				"030674058780000904154082370800060040941720065005048090508000419402801630007000002",
				"060984070047500000205736840180290734970368100000107698410079506029000000008423917",
				"460029538090800604010360790784693100623000040059007000835016079070900001940750380"
			};
		
		mediumPuzzle = new String[]
			{
				"650000070000506000014000005007009000002314700000700800500000630000201000030000097",
				"400970000600300080130005000300700004000530800070400093091000060746108200280000041",
				"020783500780014062010629000501240000970000806002900154300065009000007000100000675",
				"200100037015004860308000400081400692902605100003000705007000006020000000009267308",
				"000000010054806300000010009249001085073000900010629000000040208401068030060253000"
			};
		
		hardPuzzle = new String[]
			{
				"009000000080605020501078000000000700706040102004000000000720903090301080000000600",
				"300700258008000000057408000005000040000087502700543000004826005002070809000109000",
				"000730000000000307000004000000960070000401508000070000000200030000040619090600820",
				"500000900010000000000019040309020051020043000168005000007000168090000000000060000",
				"000000000862050430000020000000002003500060041483190002170000506000000700046510028"
			};
	}
}